/************************************************************************/
/* Game                                                                 */
/************************************************************************/
#include "Game/Managers/MarioManager.h"

namespace TD_Game {

	/************************************************************************/
	/* Default definitions                                                  */
	/************************************************************************/
	const Coord2D MarioManager::endOfMap(778, 100 + 30 + 8 + 576 + 32 + 10);


	MarioManager::MarioManager() : Manager<BaseMario>() {
		m_mariosPassed = m_goldGained = 0;
		m_pCoinSound = TD_Resources::ResourceManager::loadSound("SFX/sounds/coin.wav");
		m_finishedSpawning = true;
		m_updateMarios = true;
		m_numberOfMarios = 0;
      m_maxMarios = 0;
		m_goldColor.red = 199;
		m_goldColor.blue = 164;
		m_goldColor.green = 0;
		m_goldColor.alpha = 254;
		m_pLastSpawnedMario = NULL;
	}

	MarioManager::MarioManager(const MarioManager &rhs) : Manager<BaseMario>(rhs) {

	}
	MarioManager::~MarioManager() {
		
	}

	MarioManager & MarioManager::operator= (const MarioManager &rhs) {
		Manager<BaseMario>::operator=(rhs);
		return(*this);
	}

	void MarioManager::createGoldMessages(unsigned int creepID, BaseMario * pBaseMario) {
		if(m_goldMessages.find(creepID) == m_goldMessages.end()) {
			stringstream convertedInt;
			convertedInt << "+" << pBaseMario->getGold();
			
			m_goldMessages[creepID] = new GoldMessage(pBaseMario->getPosition() - Coord2D(6, 25), convertedInt.str(), 
				"Fonts/SuperMarioAlphabet.ttf", 18, m_goldColor, textSolid);
			m_pCoinSound->playSound(false);
		}
	}

	void MarioManager::deleteMario(map<unsigned int, BaseMario *>::iterator &iter) {
		BaseMario * pMario = NULL;
		
		

		map<string, BaseMario *>::iterator strIter = m_objStringMap.find(Utils::intToStr(iter->first));

		if (strIter == m_objStringMap.end()) {
			return;
		}

		pMario = iter->second;
		if(dynamic_cast<NormalMario *>(pMario)) {
			Utils::safeDelete<NormalMario>(dynamic_cast<NormalMario *>(pMario));
		} else if (dynamic_cast<ImmuneMario *>(pMario)) {
			Utils::safeDelete<ImmuneMario>(dynamic_cast<ImmuneMario *>(pMario));
		} else {
			Utils::safeDelete<BaseMario>(pMario);
		}
		m_objStringMap.erase(strIter);
		m_objMap.erase(iter++);
	}

	void MarioManager::configSpawning(TerrainTilemap * g_pTerrain, const Coord2D &position ,const WaveInfo &waveInfo) {
		m_startingPosition = position;
		m_endingPosition = endOfMap;
		m_spawnDirection = WEST;
		m_spawnInterval = waveInfo.spawnInterval;
		m_maxMarios = waveInfo.numberOfMarios;

		m_pTerrain = g_pTerrain;
		m_numberOfMarios = m_lastTick = 0;
		m_lastTick = waveInfo.spawnInterval + 1;

		
		m_finishedSpawning = false;
		m_pLastSpawnedMario = NULL;

		m_currWaveInfo = waveInfo;
	}

	void MarioManager::render(SDL_Surface *const g_destSurface, unsigned int deltaTime) {
		update(deltaTime);
		map<unsigned int, BaseMario *>::iterator iter;
		unsigned int i = 0;

		map<unsigned int, BaseMario *> auxiliaryMap;

		if(m_currWaveInfo.spawnType == group) {
			for(iter = m_objMap.begin(); iter != m_objMap.end(); ) {
				if(iter->second != NULL) {
					int objID = iter->second->getPosition().x + (iter->second->getPosition().y * 1000);
					auxiliaryMap[objID] = iter->second;
				}
				++iter;
			}

			for(iter = auxiliaryMap.begin(); iter != auxiliaryMap.end(); ) {
				if(iter->second != NULL) {
					if(m_updateMarios) {
						iter->second->update(deltaTime);
						iter->second->render(g_destSurface, deltaTime);
					} else {
						iter->second->render(g_destSurface, 0);
					}
					
				}
				++iter;
			}

			for(iter = m_objMap.begin(); iter != m_objMap.end(); ) {
				if(iter->second->isDying()) {
					createGoldMessages(iter->first, iter->second);
				}
				if(iter->second->isDead()) {
					m_goldGained += m_currWaveInfo.moneyPerMario;
					deleteMario(iter);
					
				} else {
					++iter;
				}
			}
		} else {
			//Renders the last element first
			for(iter = m_objMap.begin(); iter != m_objMap.end(); ) {
				if(iter->second != NULL) {
					if(m_updateMarios) {
						iter->second->update(deltaTime);
						iter->second->render(g_destSurface, deltaTime);
					} else {
						iter->second->render(g_destSurface, 0);
					}


					if(iter->second->isDying()) {
						createGoldMessages(iter->first, iter->second);
					}
					if(iter->second->isDead()) {
						m_goldGained += m_currWaveInfo.moneyPerMario;
						
						deleteMario(iter);
						
						
					} else {
						++iter;
					}
				}
			}
		}

		map<unsigned int, GoldMessage *>::iterator goldMessagesIter;

		for(goldMessagesIter = m_goldMessages.begin(); goldMessagesIter != m_goldMessages.end(); ) {
			if(m_updateMarios) {
				goldMessagesIter->second->render(g_destSurface, deltaTime);
			} else {
				goldMessagesIter->second->render(g_destSurface, 0);
			}
			if(goldMessagesIter->second->isFinished()) {
				GoldMessage *pGoldMessage = goldMessagesIter->second;
				Utils::safeDelete<GoldMessage>(pGoldMessage);
				m_goldMessages.erase(goldMessagesIter++);
			} else {
				++goldMessagesIter;
			}
		}
	}

	int MarioManager::generateSmallDeviant(unsigned int maxDeviant) {
		srand(Timer::getSystemTime());
		int yDeviant = rand() % maxDeviant;
		if(rand() % 2)
			yDeviant *= -1;
		return(yDeviant);

	}
	void MarioManager::update(int deltaTime) {
		if(m_updateMarios) {
			m_lastTick += deltaTime;
			if(m_numberOfMarios < m_maxMarios && m_spawnInterval - m_lastTick <= 0) {

				int yDeviant = 0;
				if(m_currWaveInfo.spawnType == group) {
					yDeviant = generateSmallDeviant(10);
				}

				BaseMario *pBaseMario = MarioFactory::createStandardBaseMario(m_currWaveInfo, m_startingPosition + Coord2D(0, yDeviant), yDeviant, m_pTerrain);
				createObject(pBaseMario);
				m_pLastSpawnedMario = pBaseMario;
				m_lastTick = 0;
				++m_numberOfMarios;
				
			} 

         if(m_numberOfMarios >= m_maxMarios && !m_finishedSpawning) {
            if(!m_spawnDelay.isStarted())
               m_spawnDelay.start();

            if(m_spawnDelay.getTicks() > 2000) {
               m_finishedSpawning = true;
            }
         }
        
       /*  
			if(m_numberOfMarios >= m_maxMarios && m_pLastSpawnedMario != NULL && m_pLastSpawnedMario->isWalking()) {
				m_finishedSpawning = true;
			}*/
			

			map<unsigned int, BaseMario *>::iterator iter = m_objMap.begin();
			while(iter != m_objMap.end()) {
				if(iter->second != NULL) {
					if(Coord2D::DistanceBetween(iter->second->getPosition(), m_endingPosition) < 15.0) {
						m_mariosPassed++;
						delete iter->second;
						m_objMap.erase(iter++);
					} else {
						++iter;
					}
				}
			}
		}


	}

	void MarioManager::clear() {

		
		map<unsigned int, BaseMario *>::iterator iter;
		for(iter = m_objMap.begin(); iter != m_objMap.end(); ) {
			if(iter->second != NULL) {
				delete iter->second;
			}
			m_objMap.erase(iter++);
		}

		map<unsigned int, GoldMessage *>::iterator goldMessagesIter;
		for(goldMessagesIter = m_goldMessages.begin(); goldMessagesIter != m_goldMessages.end();) {
			if(goldMessagesIter->second != NULL) {
				delete goldMessagesIter->second;
			}
			m_goldMessages.erase(goldMessagesIter++);
		}

		m_mariosPassed = m_goldGained = 0;
		m_finishedSpawning = true;
		m_updateMarios = true;
		m_lastTick = 0;
      m_numberOfMarios = 0;
      m_maxMarios = 0;
	}



	/************************************************************************/
	/* Access functions                                                     */
	/************************************************************************/

	const vector<BaseMario *> & MarioManager::getMariosWithinRadius(const Coord2D &position, unsigned int radius) {
		m_mariosWithinRadius.clear();
		map<unsigned int, BaseMario *>::const_iterator iter = getObjectMap().begin();
		while(iter != getObjectMap().end()) {
			if(Coord2D::DistanceBetween(iter->second->getPosition(), position) < radius) {
				//Selects the first creep within the radius
				if(!iter->second->isDying() && !iter->second->isDead()) {
					m_mariosWithinRadius.push_back(iter->second);
				} 
			}
			++iter;
		}
		return(m_mariosWithinRadius);
	}

}